package mysql_sync

import (
	"context"
	"encoding/json"
	"fmt"
	"gc-rds-ha/internal/common"
	"gc-rds-ha/internal/dao"
	"gc-rds-ha/internal/dao/mysql_sync"
	grpcError "gc-rds-ha/internal/error"
	"gc-rds-ha/internal/service/fetcher"
	"gc-rds-ha/internal/service/parser"
	msg "gc-rds-ha/scripts"
	"strings"
	"sync"

	"gorm.io/gorm"

	"google.golang.org/grpc/status"

	log "github.com/sirupsen/logrus"
)

type RaftStatusFormat struct {
	RaftBind       string   `json:"raftBind"`
	RaftAdvertise  string   `json:"raftAdvertise"`
	State          string   `json:"state"`
	Healthy        bool     `json:"healthy"`
	IsPartOfQuorum bool     `json:"isPartOfQuorum"`
	Leader         string   `json:"leader"`
	LeaderURI      string   `json:"leaderURI"`
	Peers          []string `json:"peers"`
}

func DoOrchSync(ctx context.Context, req *msg.STOrchSyncReq) (*msg.STOrchSyncRsp, error) {
	var (
		rsp          = &msg.STOrchSyncRsp{}
		topologyInfo []mysql_sync.TopologyInfo
		err          error
		lock         sync.Mutex //可选channel
	)
	clusterInfos, err := ClusterInfo()
	if err != nil {
		return rsp, err
	}
	for _, clusterInfo := range *clusterInfos {
		topologyInfo, err = GetSyncClusterType("").ClusterAliasInfo(clusterInfo.ClusterAlias)
		if err != nil {
			continue
		}
		if err = parser.GetAddTopology(topologyInfo, clusterInfo); err != nil {
			return rsp, err
		}
		//proxySql方式同步集群拓扑信息
		go func(clusterInfo mysql_sync.ClusterInfo) {
			defer common.GoDefer()
			lock.Lock()
			DoSyncProxySql(ctx, &msg.STSyncProxySqlReq{
				ClusterAlias: clusterInfo.ClusterAlias,
			})
		}(clusterInfo)
	}
	//fail over
	common.Go(func() {
		//GetSyncClusterType("sentinel").Recover()
	})

	return rsp, grpcError.GrpcErrOk
}

// CheckRaftStatus 集群状态检查
func (r *RaftStatusFormat) CheckRaftStatus() error {
	params := fetcher.FetchParams{
		Url:    fmt.Sprintf("%s%s", common.GetFetchUrl(), common.RaftStatus),
		Method: "get",
	}
	raftStatusRes, err := params.HttpRequest()
	if err != nil {
		return status.Error(grpcError.ErrArgsInvalid, "请求集群状态接口失败")
	}
	if err = json.Unmarshal(raftStatusRes, &r); err != nil {
		return status.Error(grpcError.ErrArgsInvalid, "检查集群状态json解析失败")
	}
	if !r.Healthy {
		return status.Error(grpcError.ErrArgsInvalid, fmt.Sprintf("集群状态: %t", r.Healthy))
	}
	r.LeaderURI = fmt.Sprintf("%s/%s", r.LeaderURI, "api")
	return nil
}

func DoOrchSwitchMaster(req *msg.STOrchSwitchMasterReq) (*msg.STOrchSwitchMasterRsp, error) {
	var (
		rsp        *msg.STOrchSwitchMasterRsp
		raftStatus = &RaftStatusFormat{}
	)
	if err := raftStatus.CheckRaftStatus(); err != nil {
		return rsp, err
	}

	autoUri := common.GracefulMasterTakeoverAuto
	if req.IsAuto == msg.Auto_Off {
		autoUri = common.GracefulMasterTakeover
	}
	params := fetcher.FetchParams{
		Url:    fmt.Sprintf("%s/%s/%s/%d/%s/%d", raftStatus.LeaderURI, autoUri, req.MasterHost, req.MasterPort, req.SlaveHost, req.SlavePort),
		Method: "get",
	}
	_, err := params.HttpRequest()
	if err != nil {
		return rsp, status.Error(grpcError.ErrArgsInvalid, "切主失败")
	}
	rsp = &msg.STOrchSwitchMasterRsp{
		Info: &msg.STOrchSwitchMasterReq{
			MasterHost: req.SlaveHost,
			MasterPort: req.SlavePort,
			SlaveHost:  req.MasterHost,
			SlavePort:  req.MasterPort,
		},
	}
	return rsp, grpcError.GrpcErrOk
}

func DoSyncProxySql(ctx context.Context, req *msg.STSyncProxySqlReq) (*msg.STSyncProxySqlRsp, error) {
	var (
		rsp = &msg.STSyncProxySqlRsp{}
	)
	// 从topology表中查到指定集群名下的节点信息
	topologyModel := &mysql_sync.MySQLClusterTopology{
		ClusterAlias: req.ClusterAlias,
	}
	topologyResult, err := topologyModel.List(common.Offset, common.Limit)
	if err != nil || len(topologyResult) <= 0 {
		log.WithFields(log.Fields{
			"topologyModel":  topologyModel,
			"topologyResult": topologyResult,
		}).Info("topologyModel.List")
		return rsp, grpcError.GrpcErrEmptyData
	}

	// 同步拓扑信息到ProxySql
	mysqlServersData := make([]*mysql_sync.MysqlServers, 0)
	for _, topology := range topologyResult {
		//
		// 节点downtime返回, 节点stop slave返回
		if isDownTime(topology) || isSlaveStop(topology) {
			continue
		}
		mysqlServersData = append(mysqlServersData, &mysql_sync.MysqlServers{
			HostName:    topology.Host,
			Port:        topology.Port,
			HostGroupID: 1, // todo hostGroupID是什么鬼？？
		})
	}
	if err = toProxysql(ctx, mysqlServersData); err != nil {
		return rsp, grpcError.GrpcErrDB
	}
	return rsp, grpcError.GrpcErrOk
}

// 可重入更新拓扑信息到proxysql
func toProxysql(ctx context.Context, d []*mysql_sync.MysqlServers) error {
	if dao.DB_PROXYSQL == nil {
		return nil
	}
	if err := dao.DB_PROXYSQL.Transaction(func(tx *gorm.DB) error {
		mysqlServers := &mysql_sync.MysqlServers{}
		//同步proxysql
		if err := mysqlServers.Create(ctx, d); err != nil {
			return grpcError.GrpcErrDB
		}
		return nil
	}); err != nil {
		return err
	}
	return nil
}

// 检查节点downtime状态
func isDownTime(c *mysql_sync.MySQLClusterTopology) bool {
	if c == nil {
		return true
	}
	discoverF, err := GetSyncClusterType("").Discover(c.Host, uint32(c.Port))
	if err != nil {
		return true
	}
	if !discoverF.Details.ReplicationIOThreadRuning {
		return true
	}
	return false
}

// 检查节点slave状态
func isSlaveStop(c *mysql_sync.MySQLClusterTopology) bool {
	if c == nil {
		return true
	}
	topologyF, err := Topology(c.Host, uint32(c.Port))
	if err != nil {
		return true
	}
	if strings.Contains(topologyF.Details, "downtimed") {
		return true
	}
	return false
}

// 发现集群
func Discover(ctx context.Context, req *msg.STDiscoverClusterReq) (*msg.STDiscoverClusterRsp, error) {
	var (
		rsp = &msg.STDiscoverClusterRsp{}
	)
	if _, err := GetSyncClusterType("").Discover(req.MasterIp, req.MasterPort); err != nil {
		return nil, err
	}
	return rsp, nil
}

func Forget(ctx context.Context, req *msg.STDiscoverClusterReq) (*msg.STDiscoverClusterRsp, error) {
	var (
		rsp = &msg.STDiscoverClusterRsp{}
	)
	if _, err := GetSyncClusterType("").Forget(req.MasterIp, req.MasterPort); err != nil {
		return nil, err
	}
	return rsp, nil
}
